/*
 * eID Diagnostics Project.
 * Copyright (C) 2010 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.eid.diagnostics;

import java.util.LinkedList;
import java.util.List;
import java.util.Observable;

import javax.swing.tree.DefaultMutableTreeNode;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import be.fedict.eid.diagnostics.annotations.TestName;
import be.fedict.eid.diagnostics.listeners.DiagnosticProgressListener;

/**
 * Diagnostic Tests Management
 * 
 * @author kevin
 *
 */
public class DiagnosticTestManager extends Observable {

	private static final Log LOG = LogFactory
			.getLog(DiagnosticTestManager.class);

	private final List<Class<? extends DiagnosticTest>> diagnosticTestClasses = new LinkedList<Class<? extends DiagnosticTest>>();;
	private DefaultMutableTreeNode gatheredDataRoot = new DefaultMutableTreeNode("Root");
	private STATE managerState;

	public static enum STATE
    {
        BEGIN_TESTING("mgr_beginning"), END_TESTING("mgr_end"), 
        TEST_STARTED("mgr_test_started"), TEST_FINISHED("mgr_test_finished"),
        IDLE("mgr_idle");
        
        private final String state;

        private STATE(String state)
        {
            this.state = state;
        }

        @Override
        public String toString()
        {
            return this.state;
        }
    };
	
    public DiagnosticTestManager() {
    	managerState = STATE.IDLE;
    }
    
    public DefaultMutableTreeNode getGatheredData() {
		return gatheredDataRoot;
	}

	public STATE getState() {
		return managerState;
	}    
    
    private void announceStateChange(STATE state) {
    	LOG.debug("Changed Manager State to: " + state);
    	managerState = state;
		setChanged();
		notifyObservers();
    }
    
    private void announceTestStateChange(STATE state, DiagnosticTest test) {
    	LOG.debug("Changed Manager State to: " + state);
    	managerState = state;
		setChanged();
		notifyObservers(test);
    }
    
	/**
	 * Registers a listener to be informed of performed tests.
	 * 
	 * @param progressListener
	 */
	public void registerProgressListener(DiagnosticProgressListener progressListener) {
		LOG.debug("New progress listener added: " + progressListener);
		addObserver(progressListener);
	}
	
	/**
	 * Registers a test to be executed.
	 * 
	 * @param diagnosticTestClass
	 */
	public void registerTest(Class<? extends DiagnosticTest> diagnosticTestClass) {
		LOG.debug("New test added: " + diagnosticTestClass.getAnnotation(TestName.class).value());
		
		// assert that the class is annotated with TestName
		TestName testNameAnnotation = diagnosticTestClass.getAnnotation(TestName.class);
		if (null == testNameAnnotation) {
			throw new RuntimeException("No TestName annotation.");
		}
		
		this.diagnosticTestClasses.add(diagnosticTestClass);
	}

	
	/**
	 * Runs all registered diagnostic tests 
	 */
	public void run() {
		
		// Announce start of test phase
		announceStateChange(STATE.BEGIN_TESTING);
		
		for (Class<? extends DiagnosticTest> diagnosticTestClass : this.diagnosticTestClasses) {
				
			DiagnosticTest test;
			try {
				test = diagnosticTestClass.newInstance();
				
				// Fetch testName
				TestName testNameAnnotation = diagnosticTestClass.getAnnotation(TestName.class);
				String testName = testNameAnnotation.value();				
				test.setName(testName);
				
				// Announce start of specific test
				announceTestStateChange(STATE.TEST_STARTED, test);
				
				// Perform test
				test.run();
				
				// Retrieve gathered data from test
				gatheredDataRoot.add(test.getGatheredData());
				
				// Announce end of specific test
				announceTestStateChange(STATE.TEST_FINISHED, test);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}			
		}
		
		// Announce end of test phase
		announceStateChange(STATE.END_TESTING);
		
	}

}
